#define JPEG_INTERNALS
#include "jinclude.h"
#include "jpeglib.h"

#undef FULL_MAIN_BUFFER_SUPPORTED

typedef struct {
  struct jpeg_c_main_controller pub; /* public fields */

  JDIMENSION cur_iMCU_row;	/* number of current iMCU row */
  JDIMENSION rowgroup_ctr;	/* counts row groups received in iMCU row */
  wxjpeg_boolean suspended;		/* remember if we suspended output */
  J_BUF_MODE pass_mode;		/* current operating mode */

  JSAMPARRAY buffer[MAX_COMPONENTS];

  #ifdef FULL_MAIN_BUFFER_SUPPORTED
  jvirt_sarray_ptr whole_image[MAX_COMPONENTS];
  #endif
} my_main_controller;

typedef my_main_controller * my_main_ptr;


/* Forward declarations */
METHODDEF( void ) process_data_simple_main
JPP( ( j_compress_ptr cinfo, JSAMPARRAY input_buf,
       JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail ) );
#ifdef FULL_MAIN_BUFFER_SUPPORTED
METHODDEF( void ) process_data_buffer_main
JPP( ( j_compress_ptr cinfo, JSAMPARRAY input_buf,
       JDIMENSION *in_row_ctr, JDIMENSION in_rows_avail ) );
#endif

METHODDEF( void )
start_pass_main( j_compress_ptr cinfo, J_BUF_MODE pass_mode ) {
  my_main_ptr mymain = ( my_main_ptr ) cinfo->main;
  /* Do nothing in raw-data mode. */
  if( cinfo->raw_data_in )
  { return; }
  mymain->cur_iMCU_row = 0;	/* initialize counters */
  mymain->rowgroup_ctr = 0;
  mymain->suspended = FALSE;
  mymain->pass_mode = pass_mode;	/* save mode for use by process_data */
  switch( pass_mode ) {
    case JBUF_PASS_THRU:
      #ifdef FULL_MAIN_BUFFER_SUPPORTED
      if( mymain->whole_image[0] != NULL )
      { ERREXIT( cinfo, JERR_BAD_BUFFER_MODE ); }
      #endif
      mymain->pub.process_data = process_data_simple_main;
      break;
      #ifdef FULL_MAIN_BUFFER_SUPPORTED
    case JBUF_SAVE_SOURCE:
    case JBUF_CRANK_DEST:
    case JBUF_SAVE_AND_PASS:
      if( mymain->whole_image[0] == NULL )
      { ERREXIT( cinfo, JERR_BAD_BUFFER_MODE ); }
      mymain->pub.process_data = process_data_buffer_main;
      break;
      #endif
    default:
      ERREXIT( cinfo, JERR_BAD_BUFFER_MODE );
      break;
  }
}

METHODDEF( void )
process_data_simple_main( j_compress_ptr cinfo,
                          JSAMPARRAY input_buf, JDIMENSION *in_row_ctr,
                          JDIMENSION in_rows_avail ) {
  my_main_ptr mymain = ( my_main_ptr ) cinfo->main;
  while( mymain->cur_iMCU_row < cinfo->total_iMCU_rows ) {
    if( mymain->rowgroup_ctr < DCTSIZE )
      ( *cinfo->prep->pre_process_data )( cinfo,
                                          input_buf, in_row_ctr, in_rows_avail,
                                          mymain->buffer, &mymain->rowgroup_ctr,
                                          ( JDIMENSION ) DCTSIZE );
    if( mymain->rowgroup_ctr != DCTSIZE )
    { return; }
    if( !( *cinfo->coef->compress_data )( cinfo, mymain->buffer ) ) {
      if( ! mymain->suspended ) {
        ( *in_row_ctr )--;
        mymain->suspended = TRUE;
      }
      return;
    }
    if( mymain->suspended ) {
      ( *in_row_ctr )++;
      mymain->suspended = FALSE;
    }
    mymain->rowgroup_ctr = 0;
    mymain->cur_iMCU_row++;
  }
}


#ifdef FULL_MAIN_BUFFER_SUPPORTED


METHODDEF( void )
process_data_buffer_main( j_compress_ptr cinfo,
                          JSAMPARRAY input_buf, JDIMENSION *in_row_ctr,
                          JDIMENSION in_rows_avail ) {
  my_main_ptr mymain = ( my_main_ptr ) cinfo->main;
  int ci;
  jpeg_component_info *compptr;
  wxjpeg_boolean writing = ( mymain->pass_mode != JBUF_CRANK_DEST );
  while( mymain->cur_iMCU_row < cinfo->total_iMCU_rows ) {
    /* Realign the virtual buffers if at the start of an iMCU row. */
    if( mymain->rowgroup_ctr == 0 ) {
      for( ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
           ci++, compptr++ ) {
        mymain->buffer[ci] = ( *cinfo->mem->access_virt_sarray )
                             ( ( j_common_ptr ) cinfo, mymain->whole_image[ci],
                               mymain->cur_iMCU_row * ( compptr->v_samp_factor * DCTSIZE ),
                               ( JDIMENSION )( compptr->v_samp_factor * DCTSIZE ), writing );
      }
      /* In a read pass, pretend we just read some source data. */
      if( ! writing ) {
        *in_row_ctr += cinfo->max_v_samp_factor * DCTSIZE;
        mymain->rowgroup_ctr = DCTSIZE;
      }
    }
    /* If a write pass, read input data until the current iMCU row is full. */
    /* Note: preprocessor will pad if necessary to fill the last iMCU row. */
    if( writing ) {
      ( *cinfo->prep->pre_process_data )( cinfo,
                                          input_buf, in_row_ctr, in_rows_avail,
                                          mymain->buffer, &mymain->rowgroup_ctr,
                                          ( JDIMENSION ) DCTSIZE );
      /* Return to application if we need more data to fill the iMCU row. */
      if( mymain->rowgroup_ctr < DCTSIZE )
      { return; }
    }
    /* Emit data, unless this is a sink-only pass. */
    if( mymain->pass_mode != JBUF_SAVE_SOURCE ) {
      if( !( *cinfo->coef->compress_data )( cinfo, mymain->buffer ) ) {
        if( ! mymain->suspended ) {
          ( *in_row_ctr )--;
          mymain->suspended = TRUE;
        }
        return;
      }
      /* We did finish the row.  Undo our little suspension hack if a previous
         call suspended; then mark the main buffer empty.
      */
      if( mymain->suspended ) {
        ( *in_row_ctr )++;
        mymain->suspended = FALSE;
      }
    }
    mymain->rowgroup_ctr = 0;
    mymain->cur_iMCU_row++;
  }
}

#endif /* FULL_MAIN_BUFFER_SUPPORTED */

void jinit_c_main_controller( j_compress_ptr cinfo, wxjpeg_boolean need_full_buffer ) {
  my_main_ptr mymain;
  int ci;
  jpeg_component_info *compptr;
  mymain = ( my_main_ptr )
           ( *cinfo->mem->alloc_small )( ( j_common_ptr ) cinfo, JPOOL_IMAGE,
                                         SIZEOF( my_main_controller ) );
  cinfo->main = ( struct jpeg_c_main_controller * ) mymain;
  mymain->pub.start_pass = start_pass_main;
  if( cinfo->raw_data_in )
  { return; }
  if( need_full_buffer ) {
    #ifdef FULL_MAIN_BUFFER_SUPPORTED
    /* Allocate a full-image virtual array for each component */
    /* Note we pad the bottom to a multiple of the iMCU height */
    for( ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
         ci++, compptr++ ) {
      mymain->whole_image[ci] = ( *cinfo->mem->request_virt_sarray )
                                ( ( j_common_ptr ) cinfo, JPOOL_IMAGE, FALSE,
                                  compptr->width_in_blocks * DCTSIZE,
                                  ( JDIMENSION ) jround_up( ( long ) compptr->height_in_blocks,
                                      ( long ) compptr->v_samp_factor ) * DCTSIZE,
                                  ( JDIMENSION )( compptr->v_samp_factor * DCTSIZE ) );
    }
    #else
    ERREXIT( cinfo, JERR_BAD_BUFFER_MODE );
    #endif
  } else {
    #ifdef FULL_MAIN_BUFFER_SUPPORTED
    mymain->whole_image[0] = NULL; /* flag for no virtual arrays */
    #endif
    /* Allocate a strip buffer for each component */
    for( ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
         ci++, compptr++ ) {
      mymain->buffer[ci] = ( *cinfo->mem->alloc_sarray )
                           ( ( j_common_ptr ) cinfo, JPOOL_IMAGE,
                             compptr->width_in_blocks * DCTSIZE,
                             ( JDIMENSION )( compptr->v_samp_factor * DCTSIZE ) );
    }
  }
}
